/** 
 * This file is part of osami-fin-demo project( http://code.google.com/p/osami-fin-demo/ )
 * @license Apache License 2.0
 */
package org.osami.appevent;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneStateListener;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.util.Log;

/** Base class for listening for phone events */
abstract class BaseReceiver extends BroadcastReceiver {

	EventDispatcher eventDispatcher;
	Logger log;

	public BaseReceiver(EventDispatcher aEventDispatcher) {
		super();
		this.eventDispatcher = aEventDispatcher;
		this.log = new Logger(this.getClass().getName());
	}
}

/**
 * Listens for phone states while the call is on and creates OSGi events of
 * them.
 */
class CallStateListener extends PhoneStateListener {

	Logger log;

	protected Context context;
	protected boolean incoming;
	protected String phoneNumber;
	protected String callerName;
	protected EventDispatcher eventDispatcher;

	public void updateCallData(Context aContext, boolean incoming,
			String phoneNumber) {
		this.context = aContext;
		this.phoneNumber = phoneNumber;
		this.incoming = incoming;

		callerName = LocalUtilities.getDisplayNameForPhoneNumber(context,
				this.phoneNumber);
	}

	public CallStateListener(EventDispatcher aEventDispatcher) {
		super();

		this.log = new Logger(this.getClass().getName());
		this.eventDispatcher = aEventDispatcher;
	}

	// TODO: HashMap?
	public static String phoneStateToEventState(int aCallState) {
		switch (aCallState) {

		case TelephonyManager.CALL_STATE_IDLE:
			return EventDispatcher.CALL_STATE_IDLE;

		case TelephonyManager.CALL_STATE_OFFHOOK:
			return EventDispatcher.CALL_STATE_OFFHOOK;

		case TelephonyManager.CALL_STATE_RINGING:
			return EventDispatcher.CALL_STATE_RINGING;
		default:
			Log.w("CallStateListener", String.format(
					"Unknown call state :%d, 0x%x", aCallState, aCallState));
		}
		return "";
	}

	public void makeEvent(String aEventType) {
		if (incoming) {
			this.eventDispatcher.makeEventCallIncoming(aEventType,
					this.phoneNumber, callerName);
		} else {
			this.eventDispatcher.makeEventCallOutgoing(aEventType,
					this.phoneNumber, callerName);
		}
	}

	public void makeEvent(int aCallState) {
		String eventType = CallStateListener.phoneStateToEventState(aCallState);
		makeEvent(eventType);
	}

	public void onCallStateChanged(int aCallState, String aIncomingNumber) {
		log.d("Got aCallState", aCallState);

		if (context == null) {
			log.e("Context not set");
			return;
		}

		// Let the receiver handle this one
		if (aCallState == TelephonyManager.CALL_STATE_IDLE) {
			return;
		}

		this.makeEvent(aCallState);
	}
}

/** Listens for phone state changes and outgoing call event */
class PhoneStateReceiver extends BaseReceiver {
	boolean isOutGoing = false;
	CallStateListener phoneListener = null;

	public PhoneStateReceiver(EventDispatcher aEventDispatcher) {
		super(aEventDispatcher);
	}

	@Override
	public void onReceive(Context aContext, Intent aIntent) {
		log.d("onReceive");

		TelephonyManager phone = LocalUtilities.getTelephony(aContext);
		if (phone == null) {
			return;
		}

		log.df("phoneState:%d", phone.getCallState());

		// The listener does not work properly with outgoing call
		// We get IDLE while waiting for answer in PhoneStateListener.
		// Works OK here so we need to check the idle state here.
		// The first IDLE comes when the call is starting(incoming\outgoing).
		// We need to start the phone listener on first IDLE and stop it on the
		// second.
		if (phone.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
			if (phoneListener != null) {

				// Assuming this happens only after a call
				phoneListener.makeEvent(EventDispatcher.CALL_STATE_IDLE);

				phone.listen(phoneListener, PhoneStateListener.LISTEN_NONE);
				phoneListener = null;
				log.d("Call listener disabled");
				return;
			}
		}

		if (phoneListener == null) {
			phoneListener = new CallStateListener(this.eventDispatcher);
			log.d("Created new CallStateListener");
		}

		if (aIntent.hasExtra(Intent.EXTRA_PHONE_NUMBER)) {
			log.d("OUTGOING call: Has extra phone number");

			String number = aIntent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
			phoneListener.updateCallData(aContext, false, number);
			phone.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);

		} else {
			String number = aIntent
					.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
			if (number != null) {
				log.d("INCOMING call", number);
				phoneListener.updateCallData(aContext, true, number);
				phone.listen(phoneListener,
						PhoneStateListener.LISTEN_CALL_STATE);
			} else {
				log.d("No number");
			}
		}
	}
}

/** Listens for SMS events and creates OSGi event about it */
class IncomingSMSReceiver extends BaseReceiver {
	public IncomingSMSReceiver(EventDispatcher aEventDispatcher) {
		super(aEventDispatcher);
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		
		Bundle bundle = intent.getExtras();

		Object messages[] = (Object[]) bundle.get("pdus");
		SmsMessage smsMessage[] = new SmsMessage[messages.length];
		for (int n = 0; n < messages.length; n++) {
			smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
		}
		
		String smsSenderNumber = new String(smsMessage[0].getOriginatingAddress());
		smsSenderNumber = smsSenderNumber.replaceFirst("\\+\\d\\d\\d", "0");
		String smsSenderName = LocalUtilities.getDisplayNameForPhoneNumber(context, smsSenderNumber);
		
		this.eventDispatcher.makeEventSMS(smsMessage[0].getMessageBody(), smsSenderNumber, smsSenderName);
	}
}